﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SQLite;
using RedFoxUtils;
using CsQuery;
using System.Diagnostics;
using PagedList;
using System.Net;
using System.Collections.Specialized;

namespace RedFoxScraper
{
  public partial class Form1 : Form
  {
    #region Fields (3) 

    //private ScraperService mService;
    private List<int> mBoardPostIds = null;
    GUIScraperCallback mScrapercallback;
    String statePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "RedFoxScraperState.txt");

    #endregion Fields 

    #region Constructors (1) 

    public Form1()
    {
      InitializeComponent();
      //mService = new ScraperService();
      InitializeBackgroundWorker();
    }

    #endregion Constructors 

    #region Methods (18) 

    // Public Methods (1) 

    public static byte[] ReadToEnd(System.IO.Stream stream)
    {
      long originalPosition = 0;

      if (stream.CanSeek)
      {
        originalPosition = stream.Position;
        stream.Position = 0;
      }

      try
      {
        byte[] readBuffer = new byte[4096];

        int totalBytesRead = 0;
        int bytesRead;

        while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
        {
          totalBytesRead += bytesRead;

          if (totalBytesRead == readBuffer.Length)
          {
            int nextByte = stream.ReadByte();
            if (nextByte != -1)
            {
              byte[] temp = new byte[readBuffer.Length * 2];
              Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
              Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
              readBuffer = temp;
              totalBytesRead++;
            }
          }
        }

        byte[] buffer = readBuffer;
        if (readBuffer.Length != totalBytesRead)
        {
          buffer = new byte[totalBytesRead];
          Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
        }
        return buffer;
      }
      finally
      {
        if (stream.CanSeek)
        {
          stream.Position = originalPosition;
        }
      }
    }
    // Private Methods (17) 

    private void btn_go_Click(object sender, EventArgs e)
    {
      btnRetrivePost.Enabled = false;
      var scraper = new Scraper(
         new HttpClient(Helper.GetUriCookieContainer(new Uri(Scraper.FORUM_BASE))),
         Database.getInstance()
      );

      if (string.IsNullOrEmpty(txt_start_page.Text) || string.IsNullOrEmpty(txt_end_page.Text))
        scraper.scrapePostDetail((Tuple<int, string>)comboBox1.SelectedItem);
      else
      {
        int startPage = 0;
        int endPage = 0;
        if (!int.TryParse(txt_start_page.Text, out startPage))
        {
          MessageBox.Show("Invalid start page");
          return;
        }
        if (!int.TryParse(txt_end_page.Text, out endPage))
        {
          MessageBox.Show("Invalid end page");
          return;
        }
        for (int i = startPage; i <= endPage; ++i)
        {
          var onePage = mBoardPostIds.ToPagedList(i, 20);
          scraper.scrapePostDetail((Tuple<int, string>)comboBox1.SelectedItem, onePage.ToList());
        }
      }

      btnRetrivePost.Enabled = true;
    }

    private void btnGrabAllPostBrief_Click(object sender, EventArgs e)
    {
      btnGrabAllPostBrief.Enabled = false;

      if (Database.getInstance().getBoards().Count == 0)
      {
        MessageBox.Show(String.Format("Click the button '{0}' first!", btnParseBorad.Text));
      }
      else
      {
        var scraper = new Scraper(
          new HttpClient(Helper.GetUriCookieContainer(new Uri(Scraper.FORUM_BASE))),
          Database.getInstance()
          );
        scraper.scrapePostsBrief();
      }

      btnGrabAllPostBrief.Enabled = true;
    }

    private void btnOneClick_Click(object sender, EventArgs e)
    {
      if (!bwScraper.IsBusy)
      {
        toolStripProgressBar.MarqueeAnimationSpeed = 100;
        toolStripStatus.Text = "開始一直抓，你可以隨時中斷。";
        btnOneClick.Text = "讓我休息一下";
        bwScraper.RunWorkerAsync();
      }
      else
      {
        toolStripProgressBar.MarqueeAnimationSpeed = 0;
        btnOneClick.Text = "一直抓一直抓";
        bwScraper.CancelAsync();
      }
    }

    private void btnParseBorad_Click(object sender, EventArgs e)
    {
      btnParseBorad.Enabled = false;

      if (Database.getInstance().getBoards().Count == 0)
      {
        var scraper = new Scraper(
          new HttpClient(Helper.GetUriCookieContainer(new Uri(Scraper.FORUM_BASE))),
          Database.getInstance()
          );
        scraper.scrapeBoard();
      }
      MessageBox.Show("Scraped.");

      btnParseBorad.Enabled = true;
    }

    private void btnRetrivePost_Click(object sender, EventArgs e)
    {
      btnRetrivePost.Enabled = false;

      var scraper = new Scraper(
        new HttpClient(Helper.GetUriCookieContainer(new Uri(Scraper.FORUM_BASE))),
        Database.getInstance()
        );
      //scraper.scrapePostDetail( 1, 51299 );
      //scraper.scrapePostDetail(1, 23763);
      // 測試抓取圖片
      scraper.scrapePostDetail(6, 42398);

      btnRetrivePost.Enabled = true;
    }

    private void btnUploadTest_Click(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() == DialogResult.OK)
      {
        string filename = openFileDialog1.FileName;
        //openFileDialog1.OpenFile
        HttpClient client = new HttpClient(Helper.GetUriCookieContainer(new Uri(Scraper.FORUM_BASE)));
        client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        System.Collections.Specialized.NameValueCollection reqparm = new System.Collections.Specialized.NameValueCollection();
        reqparm.Add("id", "2");
        // for local testing.
        //string result = client.UploadFile2("http://localhost:8080/a/i/upload", filename, reqparm);
        string result = client.UploadFile2("http://redfoxspamdb.appspot.com/a/i/upload", filename, reqparm);
        MessageBox.Show(result);
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      // REMOVEME: 暫放的 code
      MessageBox.Show(string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
      Debug.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
      Database.getInstance().updatePost(47785, "Test");
    }

    private void bwScraper_DoWork(object sender, DoWorkEventArgs e)
    {
      // Get the BackgroundWorker that raised this event.
      BackgroundWorker worker = sender as BackgroundWorker;

      // Assign the result of the computation
      // to the Result property of the DoWorkEventArgs
      // object. This is will be available to the 
      // RunWorkerCompleted eventhandler.
      e.Result = OneClickScrape(worker, e);
    }

    private void bwScraper_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
      // toolStripProgressBar's style is marquee, nothing to do.
      //toolStripProgressBar.Increment(e.ProgressPercentage);
    }

    private void bwScraper_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if (e.Error != null)
      {
        MessageBox.Show(e.Error.Message);
      }
      else if (e.Cancelled)
      {
        toolStripStatus.Text = "Canceled";
        saveScrapeProgressState();
      }
      else
      {
        toolStripStatus.Text = e.Result.ToString();
      }
    }

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
      mBoardPostIds = Database.getInstance().getPostsByBoard(((Tuple<int, string>)comboBox1.SelectedItem).Item1);
      int totalPages = (mBoardPostIds.Count / 20) + 1;
      toolStripStatus.Text = string.Format("共{0}篇文章，一頁20筆，共{1}頁", mBoardPostIds.Count, totalPages);
      txt_start_page.Text = "1";
      txt_end_page.Text = totalPages.ToString();
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
      //mService.Dispose();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      List<Tuple<int, string>> boards = Database.getInstance().getBoards();
      if (boards.Count > 0)
      {
        //BindingSource bs = new BindingSource(boards, "Item2");
        comboBox1.DataSource = boards;
        comboBox1.DisplayMember = "Item2";
        comboBox1.SelectedIndex = 0;
      }
      else
      {
        comboBox1.Enabled = false;
        btn_go.Enabled = false;
        txt_start_page.Enabled = false;
        txt_end_page.Enabled = false;
      }
    }

    private string getSavedState()
    {
      if (File.Exists(statePath))
        return File.ReadAllText(statePath);
      return "";
    }

    private void InitializeBackgroundWorker()
    {
      bwScraper.DoWork += new DoWorkEventHandler(bwScraper_DoWork);
      bwScraper.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
          bwScraper_RunWorkerCompleted);
      bwScraper.ProgressChanged += new ProgressChangedEventHandler(
          bwScraper_ProgressChanged);
    }

    private string OneClickScrape(BackgroundWorker worker, DoWorkEventArgs e)
    {
      mScrapercallback = new GUIScraperCallback(worker, getSavedState());

      var scraper = new Scraper(
          new HttpClient(Helper.GetUriCookieContainer(new Uri(Scraper.FORUM_BASE))),
          Database.getInstance(),
          mScrapercallback
          );

      if (mScrapercallback.State == Scraper.STATE.STATE_NONE)
      {
        // 1. 抓討論區
        if (Database.getInstance().getBoards().Count == 0)
        {
          scraper.scrapeBoard();
        }
        else
        {
          mScrapercallback.ReportProgress(
              RedFoxScraper.Scraper.STATE.STATE_BOARD, 0, 0);
        }
      }

      if (mScrapercallback.State == Scraper.STATE.STATE_BOARD ||
        mScrapercallback.State == Scraper.STATE.STATE_FIND_POSTS)
      {
        // 2. 抓討論區內的文章的粗略資訊
        scraper.scrapePostsBrief();
      }

      if (mScrapercallback.State >= Scraper.STATE.STATE_FIND_POSTS)
      {
        // 3. 抓各文章的內容
        scraper.scrapePostDetail();
      }

      return "成功";
    }

    private void saveScrapeProgressState()
    {
      File.WriteAllText(statePath, mScrapercallback.ToString());
    }

    #endregion Methods 

    #region Nested Classes (1) 


    class GUIScraperCallback : Scraper.IScraperCallback
    {
    #region Fields (4) 

      int mId;
      int mPage;
      Scraper.STATE mState;
      BackgroundWorker mWorker;

    #endregion Fields 

    #region Constructors (2) 

      public GUIScraperCallback(BackgroundWorker worker, string savedState)
      {
        mWorker = worker;
        _init();
        _parse(savedState);
      }

      public GUIScraperCallback(BackgroundWorker worker)
      {
        mWorker = worker;
        _init();
      }

    #endregion Constructors 

    #region Properties (3) 

      public int Id
      {
        get { return mId; }
      }

      public int Page
      {
        get { return mPage; }
      }

      public Scraper.STATE State
      {
        get { return mState; }
      }

    #endregion Properties 

    #region Methods (5) 

    // Public Methods (3) 

      public bool isCancellationPending()
      {
        return mWorker.CancellationPending;
      }

      public void ReportProgress(Scraper.STATE state, int id, int page)
      {
        Debug.WriteLine(string.Format("state={0}, id={1}, page={2}", state.ToString(), id, page));
        mState = state;
        mId = id;
        mPage = page;
      }

      public String ToString()
      {
        return string.Format("{0} {1} {2}", mState.ToString(), mId, mPage);
      }
    // Private Methods (2) 

      private void _init()
      {
        mState = Scraper.STATE.STATE_NONE;
        mId = 0;
        mPage = 0;
      }

      private void _parse(string savedState)
      {
        if (string.IsNullOrEmpty(savedState))
          return;
        string[] array = savedState.Split(new char[] { ' ' });
        if (array.Length == 3)
        {
          if (!Enum.TryParse<Scraper.STATE>(array[0], out mState))
            mState = Scraper.STATE.STATE_NONE;
          if (!int.TryParse(array[1], out mId))
            mId = 0;
          if (!int.TryParse(array[2], out mPage))
            mPage = 0;
        }
      }

    #endregion Methods 
    }
    #endregion Nested Classes 
    // CsQuery
    // https://github.com/jamietre/CsQuery
  }
}
